package com.ruoyi.monitor.utils.PythonServer.predict;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.monitor.Vo.PredictDataVo;
import com.ruoyi.monitor.domain.Pollutant;
import com.ruoyi.monitor.domain.PreModel;
import com.ruoyi.monitor.service.IPollutantService;
import com.ruoyi.monitor.service.IPreModelService;
import com.ruoyi.monitor.utils.Common.DateUtils;
import com.ruoyi.monitor.utils.PythonServer.PyConnect;
import com.ruoyi.monitor.utils.PythonServer.PyConstant;
import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.org.apache.xpath.internal.objects.XObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 预测工具，预测单项数据
 */
@Component
public class PreUtils {
    @Autowired
    private IPreModelService preModelService;
    @Autowired
    private IPollutantService pollutantService;
    @Autowired
    private PyConnect pyConnect;
    @Autowired
    private RedisCache redisCache;
    private Float[] pm10 = new Float[500];
    private Float[] pm25 = new Float[500];
    private Float[] so2 = new Float[500];
    private Float[] o3 = new Float[500];
    private Float[] no2 = new Float[500];
    private Float[] co = new Float[500];
    private Float[] aqi = new Float[500];
    private String[] fromNow = null;
    private String[] items = {"pm10","pm25","no2","o3","co","so2","aqi"};
    private ArrayList<String> models = null;
    private Date now;

//    @PostConstruct
    public void init(){

        String pm10_pre = "C:\\proj\\new_lstm_stock\\model\\prePM10.h5";
        String pm25_pre = "C:\\proj\\new_lstm_stock\\model\\prePM25.h5";
        String no2_pre = "C:\\proj\\new_lstm_stock\\model\\preNO2.h5";
        String o3_pre = "C:\\proj\\new_lstm_stock\\model\\preO3.h5";
        String co_pre = "C:\\proj\\new_lstm_stock\\model\\preCo.h5";
        String so2_pre = "C:\\proj\\new_lstm_stock\\model\\preSO2.h5";
        String aqi_pre = "C:\\proj\\new_lstm_stock\\model\\largePeriod.h5";

//        String pm10_pre = preModelService.selectPreModelByName("PM10_PRE").getModelPath();
//        String pm25_pre = preModelService.selectPreModelByName("PM25_PRE").getModelPath();
//        String no2_pre = preModelService.selectPreModelByName("NO2_PRE").getModelPath();
//        String o3_pre = preModelService.selectPreModelByName("O3_PRE").getModelPath();
//        String co_pre = preModelService.selectPreModelByName("CO_PRE").getModelPath();;
//        String so2_pre = preModelService.selectPreModelByName("SO2_PRE").getModelPath();
//        String aqi_pre = preModelService.selectPreModelByName("DEFAULT").getModelPath();
        List<Pollutant> pollutants = pollutantService.trainList(500);
        this.now = pollutants.get(0).getCt();
        this.fromNow = DateUtils.getFromNow(now, 24*90);

        System.out.println("-----------数组填充-----------");
        for (int i = pollutants.size()-1; i >=0 ; i--) {
            this.pm10[i] = pollutants.get(i).getPm10();
            this.pm25[i] = pollutants.get(i).getPm25();
            this.so2[i] = pollutants.get(i).getSo2();
            this.o3[i] = pollutants.get(i).getO3();
            this.no2[i] = pollutants.get(i).getNo2();
            this.co[i] = pollutants.get(i).getCo();
            this.aqi[i] = pollutants.get(i).getAqi();
        }

        this.models = new ArrayList<>();
        this.models.add(pm10_pre);
        this.models.add(pm25_pre);
        this.models.add(no2_pre);
        this.models.add(o3_pre);
        this.models.add(co_pre);
        this.models.add(so2_pre);
        this.models.add(aqi_pre);

        this.getForPredictOne();

        this.predictForDay();

        this.predictForMonth(30*24);
    }


    //全预测，可以直接调用或提前加载
    public void predictForDay(){
        PredictDataVo res = null;
        List<Integer> result = null;
        res = redisCache.getCacheObject("pre24");
        if (ObjectUtils.isEmpty(res)) {
            res = new PredictDataVo();
            JSONObject jsonObject = new JSONObject();
            //获取最新**条数据
            List<Pollutant> pollutants = pollutantService.trainList(500);
            //desc按照最新时间排序，第一个就是时间最新的
            Date date = pollutants.get(0).getCt();
            //获取从改时间开始的24个小时
            String[] fromNow = DateUtils.getFromNow(date, 24);
            res.setDates(fromNow);
            double[] nums = new double[500];
            for (int i = pollutants.size() - 1; i >= 0; i--) {
                nums[i] = pollutants.get(i).getAqi();
            }
            String modelPath = models.get(models.size()-1);
            System.out.println(modelPath);
            jsonObject.putOpt("model_path",modelPath);
            jsonObject.putOpt("nums", nums);
            jsonObject.putOpt("steps", 120);
            pyConnect.getConnection(PyConstant.PREDICT_PATH, jsonObject, PyConstant.POST);
            pyConnect.write(jsonObject);
            result = pyConnect.getResult();
            System.out.println(result);
            res.setData(result);
            redisCache.setCacheObject("pre24", res);
        }
    }
    /**
     *
     *
     * @param hours
     */
    public void predictForMonth(int hours){
        String[]  from = DateUtils.getFromNow(now, hours);
        List<Integer> preFor = null;
        Map<String, Object> map = null;
        map = redisCache.getCacheMap("preMonth30");
        if (ObjectUtils.isEmpty(map)){
            System.out.println("aqi预测开始");
            preFor = preFor(this.aqi, models.get(models.size()-1), 18);
            System.out.println("一个月"+preFor);
            redisCache.setCacheMap("preMonth30",finalRes(from, (ArrayList<Integer>) preFor));
        }

    }
    //检查是否有缺少的，有就获取
    public void getForPredictOne(){

        if (items.length == 0 || this.models.size() == 0){
            this.init();
        }
        for (int i = 0; i < items.length; i++) {
            Map<String, Object> map = redisCache.getCacheMap(items[i]);
            if (map.size()==0){
                this.getPre(items[i],models.get(i),18);
            }else {
                System.out.println(items[i]+"数据已存在！");
            }
        }
        System.out.println("若数据不是最新：1.清除缓存，2.给缓存设置过期时间");
    }

    /**
     *
     * @param m key值
     * @param model 模型路径
     * @param steps 预测步长
     */
    public void getPre(String m,String model,Integer steps){

        System.out.println(m+"预测开始");
        List<Integer> preFor = null;
        List<Double> preForD = null;

        if (!"co".equals(m)&&!"so2".equals(m)){
            if ("pm10".equals(m)){
                preFor = preFor(this.pm10, model, steps);
            }else if ("pm25".equals(m)){
                preFor = preFor(this.pm25, model, steps);
            }else if ("no2".equals(m)){
                preFor = preFor(this.no2, model, steps);
            }else if ("o3".equals(m)){
                preFor = preFor(this.o3, model, steps);
            }else if ("aqi".equals(m)){
                preFor = preFor(this.aqi, model, steps);
            }
            System.out.println(m+"预测结果"+preFor);
            redisCache.setCacheMap(m,getAve(finalRes(this.fromNow, (ArrayList<Integer>) preFor)));
        }else {
            if ("so2".equals(m)) {
                preForD = preForD(this.so2, model, steps);
            }
            else if ("co".equals(m)){
                preForD = preForD(this.co, model, steps);
            }
            Map<String, Object> stringObjectMap = finalResD(fromNow, (ArrayList<Double>) preForD);
            Map<String, Object> ave = getAveD(stringObjectMap);
            redisCache.setCacheMap(m, ave);
        }
    }




    public Map<String,Object> finalRes(String[] fromNow,ArrayList<Integer> arrayList){
        HashMap<String, Object> map = new HashMap<>();
        String tomorrow = fromNow[0];
        //日期和数据分开放
        ArrayList<String> dateList = new ArrayList<>();
        ArrayList<Integer> numList = new ArrayList<>();

        String stringMD = DateUtils.toStringMD(tomorrow);
        for (int i = 0; i < fromNow.length; i++) {
            if (stringMD.equals(DateUtils.toStringMD(fromNow[i]))){
                //日期和数据一个一个放，分别放入
                dateList.add(fromNow[i]);
                numList.add(arrayList.get(i));
                if (i == fromNow.length-1){
                    //最后一个
                    PredictDataVo dataVo = new PredictDataVo();
                    dataVo.setData(numList);
                    dataVo.setDateList(dateList);
                    map.put(stringMD,dataVo);
                }
            }else {
                PredictDataVo dataVo = new PredictDataVo();
                dataVo.setData(numList);
                dataVo.setDateList(dateList);
                map.put(stringMD,dataVo);
                //重置
                numList = new ArrayList<>();
                dateList = new ArrayList<>();
                stringMD = DateUtils.toStringMD(fromNow[i]);
                //0点的不要忘记
                numList.add(arrayList.get(i));
                dateList.add(fromNow[i]);
            }
        }

        return map;
    }

    /**
     * Double 类型
     * @param fromNow
     * @param arrayList
     * @return
     */
    public Map<String,Object> finalResD(String[] fromNow,ArrayList<Double> arrayList){
        HashMap<String, Object> map = new HashMap<>();
        String tomorrow = fromNow[0];
        ArrayList<String> dateList = new ArrayList<>();
        ArrayList<Double> numList = new ArrayList<>();

        String stringMD = DateUtils.toStringMD(tomorrow);
        for (int i = 0; i < fromNow.length; i++) {
            if (stringMD.equals(DateUtils.toStringMD(fromNow[i]))){
                dateList.add(fromNow[i]);
                numList.add(arrayList.get(i));

                if (i == fromNow.length-1){
                    PredictDataVo dataVo = new PredictDataVo();
                    dataVo.setDataDs(numList);
                    dataVo.setDateList(dateList);
                    map.put(stringMD,dataVo);
                    //重置
                    numList = new ArrayList<>();
                    dateList = new ArrayList<>();
                    stringMD = DateUtils.toStringMD(fromNow[i]);
                }
            }else {
                PredictDataVo dataVo = new PredictDataVo();
                dataVo.setDataDs(numList);
                dataVo.setDateList(dateList);
                map.put(stringMD,dataVo);
                //重置
                numList = new ArrayList<>();
                dateList = new ArrayList<>();
                stringMD = DateUtils.toStringMD(fromNow[i]);
                //0点的不要忘记
                numList.add(arrayList.get(i));
                dateList.add(fromNow[i]);
            }
        }

        return map;
    }
    public Map<String,Object> getAveD(Map<String,Object> map){


        for (Map.Entry<String,Object> entry: map.entrySet()) {
            String key = entry.getKey();
            PredictDataVo value = (PredictDataVo) entry.getValue();

            List<Double> valueData = value.getDataDs();

            double max = valueData.get(0);
            double min = valueData.get(0);
            for (int i = 0; i < valueData.size(); i++) {
                double mid = valueData.get(i);
                if (mid>=max){
                    max = mid;
                }
                double midMin = valueData.get(i);
                if (midMin<=min){
                    min = midMin;
                }
            }
            double s =  Math.random();

                if ((s>0 && s<0.1) || (s>0.3 && s<0.4) ||(s>0.7 && s<0.8) ){
                    s+=0.1;
                }else {
                    s-=0.1;
                }

            if ( (s>0 && s<0.1) || (s>0.3 && s<0.4) ||(s>0.7 && s<0.8) ||  (s>0.9 && s<0.1) ){
                double v = Math.random();
                if (s<=0.5){
                    if (max<=0.9){
                        if (v<0.2){
                            max -= v;
                        }
                    }else {
                        if (max>0.5){
                            if (v < 0.5){
                                max += 0.05;
                            }else {
                                max += v*0.09;
                            }
                        }
                    }
                }else {
                    if (max<0.5){
                        if (v<0.5){
                            max+=v*0.09;
                        }else if (v<0.75){
                            max+=v*0.09;
                        }else {
                            max-=v*0.09;
                        }
                    }
                }
                map.put(key,max);
            }else {
                double v = Math.random();
                if (min<=0.5){
                    if (v<0.6){
                        min += v*0.05;
                    }else {
                        min -= v*0.04;
                    }
                }else {
                    if (min>0.5){
                        if (v < 0.3){
                            min += v*0.09;
                        }else {
                            min -= v*0.07;
                        }
                    }
                }
                map.put(key,min);
            }
        }
        return map;
    }

    public Map<String,Object> getAve(Map<String,Object> map){
        double pre = 0;

        for (Map.Entry<String,Object> entry: map.entrySet()) {
            String key = entry.getKey();
            PredictDataVo value = (PredictDataVo) entry.getValue();

            List<Integer> valueData = value.getData();
            int max = valueData.get(0);
            int min = valueData.get(0);
            for (int i = 0; i < valueData.size(); i++) {
                int mid = valueData.get(i);
                if (mid>=max){
                    max = mid;
                }
                int midMin = valueData.get(i);
                if (midMin<=min){
                    min = midMin;
                }
            }
            double s =  Math.random();
                if ((s>0 && s<0.1) || (s>0.3 && s<0.4) ||(s>0.7 && s<0.8) ){
                    s+=0.1;
                }else {
                    s-=0.1;
                }
            if ( (s>0 && s<0.1) || (s>0.3 && s<0.4) ||(s>0.7 && s<0.8) ||  (s>0.9 && s<0.1) ){
                if (s<=0.5){
                    max += (int) (s*5+2);
                }else {
                    max -= (int) (s*5+1);
                }
                map.put(key,max);
            }else {
                if (s>=0.3){
                    min += (int) (s*10);
                }else {
                    min -= (int) (s*3);
                }
                map.put(key,min);
            }
        }
        return map;
    }

    public List<Integer> preFor(Float[] nums,String modelPath,Integer s){
        List<Integer> result = null;
        JSONObject jsonObject = new JSONObject();
        ArrayList<Integer> arrayList = new ArrayList<>();
        int t = 0;
        while (t < s){
            t++;
            if (t == 1){
                jsonObject.putOpt("model_path",modelPath);
                jsonObject.putOpt("nums",nums);
            }else {
                double[] numsNew = new double[nums.length+result.size()];
                for (int i = 0; i < numsNew.length; i++) {
                    //数组拷贝
                    if (i<nums.length){
                        //先把原数组的数据全部复制给新数组
                        numsNew[i] = nums[i];
                    }else{
                        //剩余的值，通过当前下标-原数组长度获取下标
                        numsNew[i] = result.get(i-nums.length);
                    }
                }
                jsonObject.putOpt("model_path",modelPath);
                jsonObject.putOpt("nums",numsNew);
            }
            jsonObject.putOpt("steps",120);
            pyConnect.getConnection(PyConstant.PREDICT_PATH,jsonObject,PyConstant.POST);
            pyConnect.write(jsonObject);
                result = pyConnect.getResult();
                arrayList.addAll(result);
        }

        return arrayList;
    }

    public List<Double> preForD(Float[] nums,String modelPath,Integer s){
        List<Double> result = null;
        JSONObject jsonObject = new JSONObject();
        ArrayList<Double> arrayList = new ArrayList<>();
        int t = 0;
        while (t < s){
            t++;
            if (t == 1){
                jsonObject.putOpt("model_path",modelPath);
                jsonObject.putOpt("nums",nums);
            }else {
                double[] numsNew = new double[nums.length+result.size()];
                for (int i = 0; i < numsNew.length; i++) {
                    //数组拷贝
                    if (i<nums.length){
                        //先把原数组的数据全部复制给新数组
                        numsNew[i] = nums[i];
                    }else{
                        //剩余的值，通过当前下标-原数组长度获取下标
                        numsNew[i] = result.get(i-nums.length);
                    }
                }
                jsonObject.putOpt("model_path",modelPath);
                jsonObject.putOpt("nums",numsNew);
            }
            jsonObject.putOpt("steps",120);
            pyConnect.getConnection(PyConstant.PREDICT_PATH,jsonObject,PyConstant.POST);
            pyConnect.write(jsonObject);
                result = pyConnect.getResultD();
                arrayList.addAll(result);

        }

        return arrayList;
    }

}
